entMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="去除水印" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xot-0B-lcT"> 621
-                                                <rect key="frame" x="200.5" y="15" width="62" height="18"/>
471
+                                                <rect key="frame" x="176.5" y="15" width="62" height="18"/>
622 472
                                                 <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
623 473
                                                 <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
624 474
                                                 <nil key="highlightedColor"/>
625 475
                                             </label>
626 476
                                         </subviews>
477
+                                        <accessibility key="accessibilityConfiguration">
478
+                                            <accessibilityTraits key="traits" image="YES"/>
479
+                                        </accessibility>
627 480
                                         <constraints>
628
-                                            <constraint firstItem="IpC-PG-FTb" firstAttribute="leading" secondItem="lCw-zE-NnH" secondAttribute="leading" id="AI8-0A-5NM"/>
629 481
                                             <constraint firstItem="pvN-rn-4Jn" firstAttribute="centerY" secondItem="lCw-zE-NnH" secondAttribute="centerY" id="IiE-3R-qra"/>
630
-                                            <constraint firstAttribute="trailing" secondItem="IpC-PG-FTb" secondAttribute="trailing" id="JXk-xw-Ijl"/>
631 482
                                             <constraint firstItem="Xot-0B-lcT" firstAttribute="leading" secondItem="pvN-rn-4Jn" secondAttribute="trailing" constant="5" id="iEp-95-h2T"/>
632 483
                                             <constraint firstItem="pvN-rn-4Jn" firstAttribute="centerX" secondItem="lCw-zE-NnH" secondAttribute="centerX" constant="-40" id="ldO-mF-P0P"/>
633
-                                            <constraint firstAttribute="bottom" secondItem="IpC-PG-FTb" secondAttribute="bottom" id="w9Z-mh-Lbw"/>
634
-                                            <constraint firstItem="IpC-PG-FTb" firstAttribute="top" secondItem="lCw-zE-NnH" secondAttribute="top" id="wZx-uT-kRL"/>
635 484
                                             <constraint firstItem="Xot-0B-lcT" firstAttribute="centerY" secondItem="pvN-rn-4Jn" secondAttribute="centerY" id="z6Y-h0-ffK"/>
636 485
                                         </constraints>
486
+                                        <connections>
487
+                                            <outletCollection property="gestureRecognizers" destination="thN-TP-atT" appends="YES" id="gKW-D0-5pd"/>
488
+                                        </connections>
637 489
                                     </view>
638 490
                                 </subviews>
639 491
                                 <constraints>
@@ -648,54 +500,64 @@
648 500
                         <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
649 501
                         <gestureRecognizers/>
650 502
                         <constraints>
651
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="BJb-h6-o9s" secondAttribute="bottom" id="73f-0Y-3Vq"/>
652
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="lID-7p-0oW" secondAttribute="bottom" constant="45" id="8XF-qX-tug"/>
653
-                            <constraint firstAttribute="trailing" secondItem="BJb-h6-o9s" secondAttribute="trailing" id="FVI-fa-ovF"/>
654
-                            <constraint firstAttribute="bottom" secondItem="M7B-fx-M7f" secondAttribute="bottom" id="HPO-e8-4k2"/>
655
-                            <constraint firstItem="BJb-h6-o9s" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="LUw-LA-s32"/>
656
-                            <constraint firstAttribute="trailing" secondItem="M7B-fx-M7f" secondAttribute="trailing" id="N1J-SK-PaC"/>
657
-                            <constraint firstItem="9RM-c8-CL6" firstAttribute="top" secondItem="n3n-4c-ZrJ" secondAttribute="bottom" id="WmR-iX-rxE"/>
658
-                            <constraint firstItem="lID-7p-0oW" firstAttribute="centerX" secondItem="DXj-L8-o9b" secondAttribute="centerX" id="X50-n0-wZy"/>
659
-                            <constraint firstItem="BJb-h6-o9s" firstAttribute="top" secondItem="aUY-hC-XIK" secondAttribute="bottom" id="YxH-gE-R4v"/>
660
-                            <constraint firstAttribute="trailing" secondItem="n3n-4c-ZrJ" secondAttribute="trailing" id="c7G-xk-PsT"/>
661
-                            <constraint firstItem="n3n-4c-ZrJ" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="cYR-vc-dTi"/>
662
-                            <constraint firstItem="M7B-fx-M7f" firstAttribute="leading" secondItem="DXj-L8-o9b" secondAttribute="leading" id="svX-zh-yc8"/>
503
+                            <constraint firstItem="fD2-Ow-gtt" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="05m-kg-yB6"/>
504
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="bottom" secondItem="lID-7p-0oW" secondAttribute="bottom" constant="45" id="8XF-qX-tug"/>
505
+                            <constraint firstAttribute="bottom" secondItem="M7B-fx-M7f" secondAttribute="bottom" constant="-56" id="HPO-e8-4k2"/>
506
+                            <constraint firstItem="fD2-Ow-gtt" firstAttribute="top" secondItem="9gv-8b-ehH" secondAttribute="top" id="IeE-A7-ZV9"/>
507
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="M7B-fx-M7f" secondAttribute="trailing" id="N1J-SK-PaC"/>
508
+                            <constraint firstAttribute="bottom" secondItem="n3n-4c-ZrJ" secondAttribute="bottom" id="WmR-iX-rxE"/>
509
+                            <constraint firstItem="lID-7p-0oW" firstAttribute="centerX" secondItem="9gv-8b-ehH" secondAttribute="centerX" id="X50-n0-wZy"/>
510
+                            <constraint firstAttribute="bottom" secondItem="fD2-Ow-gtt" secondAttribute="bottom" id="XB5-IP-h70"/>
511
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="n3n-4c-ZrJ" secondAttribute="trailing" id="c7G-xk-PsT"/>
512
+                            <constraint firstItem="n3n-4c-ZrJ" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="cYR-vc-dTi"/>
513
+                            <constraint firstItem="9gv-8b-ehH" firstAttribute="trailing" secondItem="fD2-Ow-gtt" secondAttribute="trailing" id="riO-3O-KZ0"/>
514
+                            <constraint firstItem="M7B-fx-M7f" firstAttribute="leading" secondItem="9gv-8b-ehH" secondAttribute="leading" id="svX-zh-yc8"/>
663 515
                         </constraints>
664
-                        <connections>
665
-                            <outletCollection property="gestureRecognizers" destination="thN-TP-atT" appends="YES" id="fm4-zR-Gbm"/>
666
-                        </connections>
516
+                        <viewLayoutGuide key="safeArea" id="9gv-8b-ehH"/>
667 517
                     </view>
518
+                    <size key="freeformSize" width="375" height="1000"/>
668 519
                     <connections>
669 520
                         <outlet property="buyView" destination="n3n-4c-ZrJ" id="SXL-nK-eIX"/>
521
+                        <outlet property="commentCount" destination="cuT-s1-NnA" id="m50-cn-Kcn"/>
522
+                        <outlet property="commentEditYConstraint" destination="HPO-e8-4k2" id="tfy-Rg-aiz"/>
523
+                        <outlet property="commentEditingView" destination="M7B-fx-M7f" id="ZRf-mw-WVD"/>
670 524
                         <outlet property="commentHeight" destination="HPO-e8-4k2" id="gpG-te-dKf"/>
525
+                        <outlet property="commentTableView" destination="fD2-Ow-gtt" id="jog-WM-p44"/>
671 526
                         <outlet property="commentTextField" destination="1va-ae-Juh" id="TpO-kE-PhT"/>
672
-                        <outlet property="commentView" destination="M7B-fx-M7f" id="c0Q-vg-7C9"/>
527
+                        <outlet property="enterGroupView" destination="gSr-Cm-y1W" id="bNT-Z4-eOG"/>
528
+                        <outlet property="groupAvatar" destination="nng-M9-7cj" id="Y42-Tc-QnV"/>
529
+                        <outlet property="groupName" destination="XM7-FX-tOk" id="ifP-h1-72j"/>
530
+                        <outlet property="photoCollectionView" destination="dtf-M8-otl" id="VlY-wa-ekc"/>
531
+                        <outlet property="photoTime" destination="QpI-Mp-URP" id="rJM-TG-fZW"/>
673 532
                         <outlet property="sendBtn" destination="oef-gW-sEK" id="3RV-uD-3q1"/>
674
-                        <outlet property="shuiyinImage" destination="pvN-rn-4Jn" id="SOk-vE-uCT"/>
675
-                        <outlet property="shuiyinLabel" destination="Xot-0B-lcT" id="eWR-Rv-Fzl"/>
676
-                        <outlet property="tableView" destination="BJb-h6-o9s" id="XS2-bS-jiH"/>
677
-                        <outlet property="thumbupView" destination="lID-7p-0oW" id="nBh-5u-Utm"/>
678
-                        <outlet property="waterMarkView" destination="lCw-zE-NnH" id="CPm-lw-FQT"/>
533
+                        <outlet property="thumbupCount" destination="h88-PP-cvG" id="hwu-eb-a1J"/>
534
+                        <outlet property="thumbupView" destination="Zde-8U-5Bl" id="ceB-lf-AhY"/>
535
+                        <outlet property="thumbupViewHeightConstraint" destination="fc8-6l-lEC" id="E8M-of-NHC"/>
536
+                        <outlet property="userAvatar" destination="Zj6-ve-uzJ" id="3Un-We-QLK"/>
537
+                        <outlet property="userName" destination="jmc-9F-Uzr" id="1Z5-wc-83i"/>
538
+                        <outlet property="waterMarkImage" destination="pvN-rn-4Jn" id="TaO-CX-590"/>
539
+                        <outlet property="waterMarkLabel" destination="Xot-0B-lcT" id="Jok-hr-Eeb"/>
540
+                        <outlet property="waterMarkView" destination="lCw-zE-NnH" id="z8i-gh-ZRG"/>
679 541
                     </connections>
680 542
                 </viewController>
681 543
                 <placeholder placeholderIdentifier="IBFirstResponder" id="MFn-pn-5Jb" userLabel="First Responder" sceneMemberID="firstResponder"/>
682
-                <tapGestureRecognizer id="thN-TP-atT">
544
+                <tapGestureRecognizer id="thN-TP-atT" userLabel="purchaseGestureRecognizer">
683 545
                     <connections>
684
-                        <action selector="ReturnKeyboard:" destination="qsT-Pc-Bhh" id="uVf-UL-SuP"/>
685
-                        <outlet property="delegate" destination="qsT-Pc-Bhh" id="vFS-jJ-MlO"/>
546
+                        <action selector="purchase:" destination="qsT-Pc-Bhh" id="qjs-Ot-XwA"/>
547
+                    </connections>
548
+                </tapGestureRecognizer>
549
+                <tapGestureRecognizer id="EHE-XX-kIE" userLabel="enterGroupGestureRecognizer">
550
+                    <connections>
551
+                        <action selector="enterGroup:" destination="qsT-Pc-Bhh" id="dZE-ok-iUM"/>
686 552
                     </connections>
687 553
                 </tapGestureRecognizer>
688 554
             </objects>
689
-            <point key="canvasLocation" x="-1927.2" y="1267.0164917541231"/>
555
+            <point key="canvasLocation" x="-1452" y="1137.9310344827586"/>
690 556
         </scene>
691 557
         <!--ShareController-->
692 558
         <scene sceneID="UTe-rv-qoO">
693 559
             <objects>
694 560
                 <viewController storyboardIdentifier="ShareController" automaticallyAdjustsScrollViewInsets="NO" id="KnW-jg-4H5" userLabel="ShareController" customClass="ShareController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
695
-                    <layoutGuides>
696
-                        <viewControllerLayoutGuide type="top" id="hPQ-yS-v0d"/>
697
-                        <viewControllerLayoutGuide type="bottom" id="xwf-Eg-SuC"/>
698
-                    </layoutGuides>
699 561
                     <view key="view" contentMode="scaleToFill" id="rN5-Zb-vwm">
700 562
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
701 563
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -795,10 +657,11 @@
795 657
                         </subviews>
796 658
                         <gestureRecognizers/>
797 659
                         <constraints>
798
-                            <constraint firstItem="xUW-ue-yp5" firstAttribute="leading" secondItem="rN5-Zb-vwm" secondAttribute="leading" id="Afy-jR-Jec"/>
799
-                            <constraint firstItem="xwf-Eg-SuC" firstAttribute="top" secondItem="xUW-ue-yp5" secondAttribute="bottom" constant="127" id="hco-RD-Kle"/>
800
-                            <constraint firstAttribute="trailing" secondItem="xUW-ue-yp5" secondAttribute="trailing" id="lAd-Wf-RcE"/>
660
+                            <constraint firstItem="xUW-ue-yp5" firstAttribute="leading" secondItem="C6P-7J-fWs" secondAttribute="leading" id="Afy-jR-Jec"/>
661
+                            <constraint firstItem="C6P-7J-fWs" firstAttribute="bottom" secondItem="xUW-ue-yp5" secondAttribute="bottom" constant="127" id="hco-RD-Kle"/>
662
+                            <constraint firstItem="C6P-7J-fWs" firstAttribute="trailing" secondItem="xUW-ue-yp5" secondAttribute="trailing" id="lAd-Wf-RcE"/>
801 663
                         </constraints>
664
+                        <viewLayoutGuide key="safeArea" id="C6P-7J-fWs"/>
802 665
                     </view>
803 666
                     <connections>
804 667
                         <outlet property="shareView" destination="xUW-ue-yp5" id="I5g-Zk-9uW"/>
@@ -806,22 +669,18 @@
806 669
                 </viewController>
807 670
                 <placeholder placeholderIdentifier="IBFirstResponder" id="9LO-35-FRH" userLabel="First Responder" sceneMemberID="firstResponder"/>
808 671
             </objects>
809
-            <point key="canvasLocation" x="649.60000000000002" y="1430.2848575712146"/>
672
+            <point key="canvasLocation" x="-513" y="1287"/>
810 673
         </scene>
811
-        <!--ShowFullPicController-->
674
+        <!--PhotoPreviewViewController-->
812 675
         <scene sceneID="yhk-2u-fiu">
813 676
             <objects>
814
-                <viewController storyboardIdentifier="ShowFullPicController" automaticallyAdjustsScrollViewInsets="NO" id="p3y-A2-QU1" userLabel="ShowFullPicController" customClass="ShowFullPicController" customModule="PaiAi" sceneMemberID="viewController">
815
-                    <layoutGuides>
816
-                        <viewControllerLayoutGuide type="top" id="LdU-do-COB"/>
817
-                        <viewControllerLayoutGuide type="bottom" id="uHN-Ad-PoZ"/>
818
-                    </layoutGuides>
677
+                <viewController storyboardIdentifier="PhotoPreviewViewController" automaticallyAdjustsScrollViewInsets="NO" id="p3y-A2-QU1" userLabel="PhotoPreviewViewController" customClass="PhotoPreviewViewController" customModule="Paiai_iOS" customModuleProvider="target" sceneMemberID="viewController">
819 678
                     <view key="view" contentMode="scaleToFill" id="MdC-Fu-zFL">
820 679
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
821 680
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
822 681
                         <subviews>
823
-                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" indicatorStyle="white" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="cvI-jg-TrD">
824
-                                <rect key="frame" x="0.0" y="20" width="395" height="647"/>
682
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" indicatorStyle="white" dataMode="prototypes" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cvI-jg-TrD">
683
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
825 684
                                 <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="nE7-Ce-1KB">
826 685
                                     <size key="itemSize" width="237.5" height="357"/>
827 686
                                     <size key="headerReferenceSize" width="0.0" height="0.0"/>
@@ -830,7 +689,7 @@
830 689
                                 </collectionViewFlowLayout>
831 690
                                 <cells>
832 691
                                     <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="imageCell" id="PAU-eQ-c9k" customClass="ImageCell" customModule="Paiai_iOS" customModuleProvider="target">
833
-                                        <rect key="frame" x="0.0" y="145" width="237.5" height="357"/>
692
+                                        <rect key="frame" x="0.0" y="155" width="237.5" height="357"/>
834 693
                                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
835 694
                                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
836 695
                                             <rect key="frame" x="0.0" y="0.0" width="237.5" height="357"/>
@@ -856,35 +715,31 @@
856 715
                                         </connections>
857 716
                                     </collectionViewCell>
858 717
                                 </cells>
859
-                                <connections>
860
-                                    <outlet property="dataSource" destination="p3y-A2-QU1" id="gOQ-91-JoU"/>
861
-                                    <outlet property="delegate" destination="p3y-A2-QU1" id="Woc-UQ-V73"/>
862
-                                </connections>
863 718
                             </collectionView>
864
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BRP-J0-WGF">
719
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BRP-J0-WGF" userLabel="button group">
865 720
                                 <rect key="frame" x="0.0" y="623" width="375" height="44"/>
866 721
                                 <subviews>
867
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q90-2h-mGx">
868
-                                        <rect key="frame" x="231" y="-26" width="96" height="96"/>
869
-                                        <state key="normal" image="大图模式-下载"/>
870
-                                        <connections>
871
-                                            <action selector="load" destination="p3y-A2-QU1" eventType="touchUpInside" id="tC2-9R-1be"/>
872
-                                        </connections>
873
-                                    </button>
874
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2r6-s1-9be">
722
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2r6-s1-9be" userLabel="back">
875 723
                                         <rect key="frame" x="43.5" y="-26" width="96" height="96"/>
876
-                                        <state key="normal" image="back"/>
724
+                                        <state key="normal" image="navigation-back"/>
877 725
                                         <connections>
878 726
                                             <action selector="back" destination="p3y-A2-QU1" eventType="touchUpInside" id="xKk-c3-Iub"/>
879 727
                                         </connections>
880 728
                                     </button>
881
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aOC-mu-785">
729
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aOC-mu-785" userLabel="rotate">
882 730
                                         <rect key="frame" x="139.5" y="-26" width="96" height="96"/>
883
-                                        <state key="normal" image="旋转"/>
731
+                                        <state key="normal" image="BTN-rotate"/>
884 732
                                         <connections>
885 733
                                             <action selector="rotateTheImage:" destination="p3y-A2-QU1" eventType="touchUpInside" id="LiB-TG-UYL"/>
886 734
                                         </connections>
887 735
                                     </button>
736
+                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Q90-2h-mGx" userLabel="download">
737
+                                        <rect key="frame" x="231" y="-26" width="96" height="96"/>
738
+                                        <state key="normal" image="BTN-download"/>
739
+                                        <connections>
740
+                                            <action selector="download:" destination="p3y-A2-QU1" eventType="touchUpInside" id="cEE-Yt-FWf"/>
741
+                                        </connections>
742
+                                    </button>
888 743
                                 </subviews>
889 744
                                 <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
890 745
                                 <constraints>
@@ -902,14 +757,15 @@
902 757
                         </subviews>
903 758
                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
904 759
                         <constraints>
905
-                            <constraint firstItem="uHN-Ad-PoZ" firstAttribute="top" secondItem="cvI-jg-TrD" secondAttribute="bottom" id="F0H-Mg-CBe"/>
906
-                            <constraint firstItem="uHN-Ad-PoZ" firstAttribute="top" secondItem="BRP-J0-WGF" secondAttribute="bottom" id="HMX-tH-TKt"/>
907
-                            <constraint firstItem="cvI-jg-TrD" firstAttribute="top" secondItem="MdC-Fu-zFL" secondAttribute="topMargin" id="bET-rg-QJH"/>
908
-                            <constraint firstItem="cvI-jg-TrD" firstAttribute="leading" secondItem="MdC-Fu-zFL" secondAttribute="leading" id="bxo-Ri-B7l"/>
909
-                            <constraint firstAttribute="trailing" secondItem="BRP-J0-WGF" secondAttribute="trailing" id="es7-Qg-qLl"/>
910
-                            <constraint firstItem="BRP-J0-WGF" firstAttribute="leading" secondItem="MdC-Fu-zFL" secondAttribute="leading" id="nES-ms-zxG"/>
911
-                            <constraint firstAttribute="trailing" secondItem="cvI-jg-TrD" secondAttribute="trailing" constant="-20" id="y4O-SP-QE3"/>
760
+                            <constraint firstAttribute="bottom" secondItem="cvI-jg-TrD" secondAttribute="bottom" id="F0H-Mg-CBe"/>
761
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="bottom" secondItem="BRP-J0-WGF" secondAttribute="bottom" id="HMX-tH-TKt"/>
762
+                            <constraint firstItem="cvI-jg-TrD" firstAttribute="top" secondItem="MdC-Fu-zFL" secondAttribute="top" id="bET-rg-QJH"/>
763
+                            <constraint firstItem="cvI-jg-TrD" firstAttribute="leading" secondItem="pxb-BY-KAJ" secondAttribute="leading" id="bxo-Ri-B7l"/>
764
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="trailing" secondItem="BRP-J0-WGF" secondAttribute="trailing" id="es7-Qg-qLl"/>
765
+                            <constraint firstItem="BRP-J0-WGF" firstAttribute="leading" secondItem="pxb-BY-KAJ" secondAttribute="leading" id="nES-ms-zxG"/>
766
+                            <constraint firstItem="pxb-BY-KAJ" firstAttribute="trailing" secondItem="cvI-jg-TrD" secondAttribute="trailing" id="y4O-SP-QE3"/>
912 767
                         </constraints>
768
+                        <viewLayoutGuide key="safeArea" id="pxb-BY-KAJ"/>
913 769
                     </view>
914 770
                     <connections>
915 771
                         <outlet property="collectionView" destination="cvI-jg-TrD" id="1eT-ax-Cma"/>
@@ -921,23 +777,22 @@
921 777
         </scene>
922 778
     </scenes>
923 779
     <resources>
924
-        <image name="BTN-分享" width="138" height="138"/>
925
-        <image name="BTN-评论" width="138" height="138"/>
926
-        <image name="Oval 491" width="192" height="192"/>
927
-        <image name="back" width="36" height="72"/>
928
-        <image name="icon-记录" width="36" height="36"/>
929
-        <image name="icon-评论" width="36" height="36"/>
930
-        <image name="icon-赞" width="36" height="36"/>
931
-        <image name="list-arrow" width="16" height="16"/>
780
+        <image name="BTN-comment" width="138" height="138"/>
781
+        <image name="BTN-download" width="96" height="96"/>
782
+        <image name="BTN-rotate" width="96" height="96"/>
783
+        <image name="BTN-share" width="138" height="138"/>
784
+        <image name="BTN-thumbup" width="192" height="192"/>
785
+        <image name="defaultAvatar" width="240" height="240"/>
786
+        <image name="icon-comment" width="36" height="36"/>
787
+        <image name="icon-thumbup" width="36" height="36"/>
788
+        <image name="icon-time" width="36" height="36"/>
789
+        <image name="list-arrow" width="24" height="36"/>
790
+        <image name="navigation-back" width="36" height="72"/>
932 791
         <image name="分享-QQ" width="162" height="162"/>
933 792
         <image name="分享-微信好友" width="162" height="162"/>
934 793
         <image name="分享-微博" width="162" height="162"/>
935 794
         <image name="分享-朋友圈" width="162" height="162"/>
936
-        <image name="去除水印" width="8" height="144"/>
937
-        <image name="大图模式-下载" width="96" height="96"/>
938
-        <image name="旋转" width="96" height="96"/>
939 795
         <image name="购买-去水印" width="96" height="96"/>
940 796
         <image name="进入群" width="114" height="60"/>
941
-        <image name="默认头像" width="80" height="80"/>
942 797
     </resources>
943 798
 </document>

+ 2 - 2
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailCommentCell.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  DetailCommentCell.swift
2
+//  PhotoDetailCommentCell.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/8.
@@ -10,7 +10,7 @@ import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12 12
 
13
-class DetailCommentCell: UITableViewCell {
13
+class PhotoDetailCommentCell: UITableViewCell {
14 14
 
15 15
     // MARK: Storyboard property
16 16
     @IBOutlet weak var headImage: UIImageView!

+ 49 - 1
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailCoordinator.swift

@@ -7,9 +7,57 @@
7 7
 //
8 8
 
9 9
 import Foundation
10
+import PaiaiDataKit
11
+
12
+final class PhotoDetailCoordinator: Coordinator {
13
+    let navigationController: UINavigationController
14
+    let photoDetailViewController: PhotoDetailViewController
15
+    let shareListViewModel: PhotoDetailListViewModel
16
+    
17
+    fileprivate var coordinators = [String: Coordinator]()
18
+    
19
+    init(_ photoDetailVC: PhotoDetailViewController,
20
+         nav: UINavigationController,
21
+         viewModel: PhotoDetailViewModel,
22
+         listViewModel: PhotoDetailListViewModel) {
23
+        photoDetailViewController = photoDetailVC
24
+        shareListViewModel = listViewModel
25
+        navigationController = nav
26
+        photoDetailViewController.listViewModel = shareListViewModel
27
+        photoDetailViewController.viewModel = viewModel
28
+        
29
+        viewModel.delegate = self
30
+        shareListViewModel.synchronization = viewModel
31
+        shareListViewModel.delegate = self
32
+    }
33
+    
34
+    func start() {
35
+        
36
+    }
37
+}
38
+
39
+extension PhotoDetailCoordinator: PhotoDetailViewModelDelegate {
40
+    func navigateToGroup(_ item: GroupItem) {
41
+        let vc = GroupViewController.instantiate()
42
+        vc.viewModel = GroupViewModel(groupItem: item)
43
+        let coordinator = GroupCoordinator(vc,
44
+                                           navigationController: navigationController)
45
+        coordinators["group"] = coordinator
46
+        
47
+        navigationController.pushViewController(vc)
48
+    }
49
+}
50
+
51
+extension PhotoDetailCoordinator: PhotoDetailListViewModelDelegate {
52
+    func didSelected() {
53
+        let vc = UIStoryboard.photoDetail.instantiateController(PhotoPreviewViewController.self)
54
+        vc.viewModel = shareListViewModel
55
+        navigationController.pushViewController(vc, animated: true)
56
+    }
57
+}
10 58
 
11 59
 extension UIStoryboard {
12 60
     static var photoDetail: UIStoryboard {
13
-        return UIStoryboard(name: "Detail", bundle: Bundle(identifier: "com.Paiai-iOS"))
61
+        return UIStoryboard(name: "PhotoDetail", bundle: Bundle(identifier: "com.Paiai-iOS"))
14 62
     }
15 63
 }

+ 1 - 1
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailImageCell.swift

@@ -9,5 +9,5 @@
9 9
 import UIKit
10 10
 
11 11
 class PhotoDetailImageCell: UICollectionViewCell {
12
-    
12
+    @IBOutlet weak var imageView: UIImageView!
13 13
 }

+ 265 - 339
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoDetailViewController.swift

@@ -9,6 +9,7 @@
9 9
 import UIKit
10 10
 import RxSwift
11 11
 import RxCocoa
12
+import RxDataSources
12 13
 import PaiaiDataKit
13 14
 import PaiaiUIKit
14 15
 
@@ -17,6 +18,7 @@ let kPhotographerMark = 1
17 18
 
18 19
 final class PhotoDetailViewController: UIViewController {
19 20
 
21
+    @IBOutlet weak var enterGroupView: UIView!
20 22
     @IBOutlet weak var groupAvatar: UIImageView!
21 23
     @IBOutlet weak var groupName: UILabel!
22 24
     
@@ -30,409 +32,333 @@ final class PhotoDetailViewController: UIViewController {
30 32
     @IBOutlet weak var thumbupView: UIView!
31 33
     
32 34
     @IBOutlet weak var commentCount: UILabel!
33
-    @IBOutlet weak var tableView: UITableView!
35
+    @IBOutlet weak var commentTableView: UITableView!
34 36
     
35
-    @IBOutlet weak var commentView: UIView!
37
+    @IBOutlet weak var commentEditingView: UIView!
36 38
     @IBOutlet weak var commentHeight: NSLayoutConstraint!
37 39
     @IBOutlet weak var commentTextField: UITextField!
38 40
     @IBOutlet weak var sendBtn: UIButton!
39 41
     
40 42
     @IBOutlet weak var buyView: UIView!
43
+    @IBOutlet weak var waterMarkView: UIView!
41 44
     @IBOutlet weak var waterMarkImage: UIImageView!
42 45
     @IBOutlet weak var waterMarkLabel: UILabel!
43
-
46
+    
47
+    @IBOutlet weak var thumbupViewHeightConstraint: NSLayoutConstraint!
48
+    @IBOutlet weak var commentEditYConstraint: NSLayoutConstraint!
49
+    
44 50
     // MARK: data property
45 51
     var viewModel: PhotoDetailViewModel!
46
-    lazy var datas = [PhotoItem]()
47
-    lazy var currentPhotoIndex = 0
48
-    var isHiddenEnterView = false
52
+    var listViewModel: PhotoDetailListViewModel!
53
+    
49 54
     let disposeBag = DisposeBag()
50
-    static let storyboardCtl = UIStoryboard.photoDetail.instantiateInitialViewController() as! PhotoDetailViewController
51 55
 
52 56
     // MARK: view function
53 57
     override func viewDidLoad() {
54 58
         super.viewDidLoad()
55
-//        detailPageViewModel.tipDelegate = self
56
-//        tableView.tableFooterView = UIView()
57
-        configureNotification()
58
-        
59
-        commentTextField.rx.text
60
-                        .map {!($0?.isEmpty)!}
61
-                        .bind(to: sendBtn.rx.isEnabled)
62
-                        .disposed(by: disposeBag)
59
+        binding()
60
+        setup()
61
+    }
62
+    
63
+    func setup() {
64
+        setupCommentTextField()
65
+        setupWaterMarkView()
63 66
     }
64 67
     
65 68
     func setupCommentTextField() {
66 69
         commentTextField.addLeftPadding(7)
67 70
     }
71
+    
72
+    func setupWaterMarkView() {
73
+        guard let image = UIImage.PhotoDetail.purchaseBackground else { return }
74
+        waterMarkView.backgroundColor = UIColor(patternImage: image)
75
+    }
68 76
 
69 77
     override func viewWillAppear(_ animated: Bool) {
70 78
         super.viewWillAppear(true)
71
-//        titleWithbackBar = "详情"
72
-        navigationController?.isNavigationBarHidden = false
73
-//        refreshUI(index: currentPhotoIndex)
74
-    }
75
-
76
-//    override func backToController() {
77
-//        navigationController?.popViewController(animated: true)
78
-//        if let last = navigationController?.viewControllers[(navigationController?.viewControllers.count)! - 1] as? HomeViewController {
79
-////            last.mainViewModel.models.value = datas
80
-//        }
81
-//
82
-//        if let last = navigationController?.viewControllers[(navigationController?.viewControllers.count)! - 1] as? GroupViewController {
83
-////            last.MineGroupViewModel.models.value = datas
84
-//        }
85
-//    }
86
-
87
-    func configureNotification() {
88
-        do {
89
-//            NotificationCenter.default.rx.notification(Notification.Name(rawValue: WXPayDidFinishNotification)).asObservable().subscribe { (notification) in
90
-//                FFToastView.showLoadingToast(inView: UIApplication.shared.keyWindow!, blockSuperView: true)
91
-//                self.detailPageViewModel.handleResult(errorCode: 0, success: {[weak self](PhotoItem) in
92
-//                    if let weakself = self {
93
-//                        weakself.datas[weakself.currentPhotoIndex].murl = PhotoItem.murl
94
-//                        weakself.datas[weakself.currentPhotoIndex].rurl = PhotoItem.rurl
95
-////                        weakself.showBuyView()
96
-//                        weakself.tableView.reloadRows(at: [IndexPath(item: 0, section: 1)], with: .none)
97
-//                        let fullPicCtl = UIStoryboard.detailBoard.instantiateController(ShowFullPicController.self)
98
-//                        fullPicCtl.datas = weakself.datas
99
-//                        fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
100
-//                        fullPicCtl.showNomark = weakself.detailPageViewModel.watermarkPrice != -1
101
-//                        fullPicCtl.showHD = weakself.detailPageViewModel.hdPrice != -1
102
-//                        weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
103
-//                   }
104
-//                })
105
-//                }.disposed(by: disposeBag)
106
-        }
107
-        do {
108
-//            NotificationCenter.default.rx.notification(Notification.Name.UIKeyboardWillShow)
109
-//                .asObservable()
110
-//                .subscribe({ (notification) in
111
-//                    guard let info = notification.element?.userInfo, let avalue = info[UIKeyboardFrameEndUserInfoKey] else {
112
-//                        return
113
-//                    }
114
-//
115
-//                    let height = (avalue as AnyObject).cgRectValue.size.height
116
-//                    self.returnKeyboarAction(notification.element!, height: height)
117
-//                }).disposed(by: disposeBag)
118
-        }
119
-
120
-        do {
121
-            NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)
122
-                .asObservable()
123
-                .subscribe({ (notification) in
124
-                    self.returnKeyboarAction(notification.element!, height: 0)
125
-                }).disposed(by: disposeBag)
126
-        }
127
-
79
+        viewModel.viewWillAppear.accept(())
128 80
     }
81
+}
129 82
 
130
-    // MARK: refresh interface
131
-    func refreshUI(index: Int) {
132
-        currentPhotoIndex = index
133
-//        detailPageViewModel.currentPhoto = datas[index]
134
-
135
-//        detailPageViewModel.fetchThumbup(success: {[weak self] in
136
-//            if let weakself = self {
137
-//                var model = weakself.datas[index]
138
-//                model.thumbup_num =  weakself.detailPageViewModel.thumbups.count
139
-//                weakself.datas[index] = model
140
-////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: model)
141
-//                weakself.reloadSection(inter: 3)
142
-//            }
143
-//        })
144
-//        detailPageViewModel.fetchComment(success: {[weak self] in
145
-//            if let weakself = self {
146
-//
147
-//                var model = weakself.datas[index]
148
-//                model.comment_num =  weakself.detailPageViewModel.comments.count
149
-//                weakself.datas[index] = model
150
-////                PhotoLocalStorage.instance.updateLocalData(PhotoItem: model)
151
-//                weakself.reloadSection(inter: 4)
152
-//            }
153
-//        })
83
+//MARK textField delegate
84
+extension PhotoDetailViewController: UIGestureRecognizerDelegate {
85
+    // MARK: textField
154 86
 
155
-//        reloadSection(inter: 0)
156
-//        reloadSection(inter: 2)
157
-        showBuyView()
87
+    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
88
+        return commentTextField.isFirstResponder
158 89
     }
90
+    
91
+}
159 92
 
160
-    func showBuyView() {
161
-//        if detailPageViewModel.currentPhoto.photo_from == kPhotographerMark && detailPageViewModel.currentPhoto.display_payment_btn == 1 {
162
-//            buyView.isHidden = false
163
-//            detailPageViewModel.hdPrice = -0.01
164
-//            detailPageViewModel.watermarkPrice = -0.01
165
-//            shuiyinLabel.text = !detailPageViewModel.currentPhoto.murl.isEmpty ? "查看无水印图" : "去除水印"
166
-//            shuiyinImage.isHidden = false
167
-//        } else {
168
-//            buyView.isHidden = true
169
-//        }
170
-    }
171 93
 
172
-    // MARK: Storyboard  button function
173
-    @IBAction func HDPay(_ sender: UIButton) {
174
-//        detailPageViewModel.getHD (getPriceSuccess: { [weak self] (isExist) in
175
-//            if let weakself = self {
176
-//                if isExist {
177
-//                    let fullPicCtl = UIStoryboard(name: "Detail", bundle: nil).instantiateController(ShowFullPicController.self)
178
-//                    fullPicCtl.datas = [weakself.datas[weakself.currentPhotoIndex]]
179
-//                    fullPicCtl.showNomark = true
180
-//                    fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
181
-//                    weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
182
-//                } else {
183
-//                }
184
-//            }
185
-//        })
94
+/// bind storyboard gesture action
95
+extension PhotoDetailViewController {
96
+    @IBAction func purchase(_ sender: UITapGestureRecognizer) {
97
+        
186 98
     }
187
-
188
-    @IBAction func waterMarkPay(_ sender: UIButton) {
189
-
190
-//        detailPageViewModel.getWatermark (getPriceSuccess: { [weak self] (isExist) in
191
-//            if let weakself = self {
192
-//                if isExist {
193
-//                    let fullPicCtl = UIStoryboard(name: "Detail", bundle: nil).instantiateController(ShowFullPicController.self)
194
-//                    fullPicCtl.datas = weakself.datas
195
-//                    fullPicCtl.showNomark = true
196
-//                    fullPicCtl.currentPhotoIndex = weakself.currentPhotoIndex
197
-//                    weakself.navigationController?.pushViewController(fullPicCtl, animated: true)
198
-//                } else {
199
-//                    weakself.shuiyinImage.isHidden = true
200
-//                    weakself.shuiyinLabel.text = "¥\((weakself.detailPageViewModel.watermarkPrice/100))"
201
-//                }
202
-//            }
203
-//        })
99
+    
100
+    @IBAction func enterGroup(_ sender: UITapGestureRecognizer) {
101
+        self.viewModel.navigateToGroup()
204 102
     }
103
+}
205 104
 
105
+/// bind storyboard button action
106
+extension PhotoDetailViewController {
206 107
     @IBAction func share() {
207 108
         let ctl = UIStoryboard.photoDetail.instantiateController(ShareController.self)
208 109
         ctl.shareContent = "我使用拍爱分享了一张美图,你也快来试试吧"
209
-        ctl.shareImgUrlThumb = datas[currentPhotoIndex].photo_thumbnail_url
210
-        ctl.shareUrl = datas[currentPhotoIndex].photo_share_url
110
+        //        ctl.shareImgUrlThumb = datas[currentPhotoIndex].photo_thumbnail_url
111
+        //        ctl.shareUrl = datas[currentPhotoIndex].photo_share_url
211 112
         presentController(ctl)
212 113
     }
213
-
114
+    
214 115
     @IBAction func comment() {
215
-        commentView.isHidden = false
216 116
         commentTextField.becomeFirstResponder()
117
+        
217 118
     }
218
-
119
+    
219 120
     @IBAction func sendComment() {
220
-        guard let text = commentTextField.text else {
221
-            return
222
-        }
223
-//        detailPageViewModel.sendComment(content: text, success: { [weak self] in
224
-//            if let weakself = self {
225
-//                weakself.commentTextField.text = ""
226
-//                weakself.commentTextField.resignFirstResponder()
227
-//                weakself.commentView.isHidden = true
228
-//                weakself.refreshUI(index: weakself.currentPhotoIndex)
229
-//            }
230
-//        })
121
+        guard let text = commentTextField.text else { return }
122
+        
123
+        viewModel.submitComment(text: text)
124
+        commentTextField.resignFirstResponder()
231 125
     }
232
-
126
+    
233 127
     @IBAction func thumbup() {
234
-//        detailPageViewModel.sendThumbup(success: {[weak self]  in
235
-//            if let weakself = self {
236
-//                weakself.refreshUI(index: weakself.currentPhotoIndex)
237
-//            }
238
-//
239
-//        })
128
+        viewModel.submitThumbup()
240 129
     }
130
+}
241 131
 
242
-    // MARK: custom function
243
-    func reloadSection(inter: Int) {
244
-        tableView.beginUpdates()
245
-        let indexSet = IndexSet(integer: inter)
246
-        tableView.reloadSections(indexSet, with: .none)
247
-        tableView.endUpdates()
132
+/// bind rx
133
+extension PhotoDetailViewController {
134
+    
135
+    var commentTableViewDataSource: RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoCommentItem>> {
136
+        return RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoCommentItem>>(configureCell: { (dataSource, tableView, indexPath, item) in
137
+            let cell = tableView.dequeueReusableCell(withIdentifier: "photoDetailCommentCell", for: indexPath) as! PhotoDetailCommentCell
138
+            cell.setInfo(item)
139
+            return cell
140
+        })
248 141
     }
249
-
250
-    @objc func showThumps() {
251
-//        if detailPageViewModel.thumbupsCount > 0 {
252
-//            detailPageViewModel.thumbupsCount = 0
253
-//        } else {
254
-//            detailPageViewModel.thumbupsCount = detailPageViewModel.thumbups.count
255
-//        }
256
-        reloadSection(inter: 3)
257
-    }
258
-    @objc func showComments() {
259
-//        if detailPageViewModel.commentsCount > 0 {
260
-//            detailPageViewModel.commentsCount = 0
261
-//        } else {
262
-//            detailPageViewModel.commentsCount = detailPageViewModel.comments.count
263
-//        }
264
-        reloadSection(inter: 4)
142
+    
143
+    var photoCollectionViewDataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>> {
144
+        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>>(configureCell: { (dataSource, collectionView, indexPath, item) in
145
+            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoDetailImageCell", for: indexPath) as! PhotoDetailImageCell
146
+            cell.imageView.setImage(item.photo_thumbnail2_url, placeholder: UIImage.photoPlaceholder)
147
+            return cell
148
+        })
265 149
     }
266
-
267
-    @objc func loadReportController() {
268
-//        let ctl = UIStoryboard.photoDetail.instantiateController(ReportController.self)
269
-//        presentController(ctl)
150
+    
151
+    func binding() {
152
+        bindEnterGroupViewHiddenState()
153
+        bindViewModelToGroupName()
154
+        bindViewModelToGroupAvatar()
155
+        
156
+        bindViewModelToUserName()
157
+        bindgingViewModelToUserAvatar()
158
+        
159
+        bindViewModelToThumbupCount()
160
+        bindViewModelToThumbupView()
161
+        
162
+        bindViewModelToCommentCount()
163
+        bindViewModelToCommentTableView()
164
+        
165
+        bindBuyViewIsVisiable()
166
+        
167
+        bindCommentTextFieldToSendBtn()
168
+        
169
+        bindCollectionViewDelegate()
170
+        bindCollectionViewSelected()
171
+        bindCollectionViewToListViewModel()
172
+        bindListViewModelToCollectionView()
173
+        
174
+        bindViewWillAppear()
175
+        
176
+        monitorKeyboardWillShow()
177
+        monitorKeyboardWillHide()
270 178
     }
271
-
272
-    func returnKeyboarAction(_ notification: Notification, height: CGFloat) {
273
-        guard let info = (notification as NSNotification).userInfo, let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
274
-            return
275
-        }
276
-
277
-        UIView.animate(withDuration: duration, animations: {() -> Void in
278
-                self.commentHeight.constant = height
279
-                self.commentView.superview!.layoutIfNeeded()
280
-        })
179
+    
180
+    func bindEnterGroupViewHiddenState() {
181
+        viewModel.isHiddenEnterGroupBtn.bind(to: enterGroupView.rx.isHidden).disposed(by: disposeBag)
281 182
     }
282
-
283
-    // MARK: deinit
284
-    deinit {
285
-        NotificationCenter.default.removeObserver(self)
183
+    
184
+    func bindViewModelToGroupName() {
185
+        viewModel.groupName.bind(to: groupName.rx.text).disposed(by: disposeBag)
286 186
     }
287
-
288
-}
289
-
290
-// MARK: custom delegate function
291
-extension PhotoDetailViewController: CellDelegate {
292
-    func selectIndex(indexpath: IndexPath) {
293
-        let ctl = UIStoryboard.photoDetail.instantiateController(ShowFullPicController.self)
294
-        ctl.datas = datas
295
-        ctl.currentPhotoIndex = currentPhotoIndex
296
-        show(ctl, sender: nil)
187
+    
188
+    func bindViewModelToGroupAvatar() {
189
+        viewModel.groupAvatar
190
+            .subscribe(onNext: {[weak self] (avatar) in
191
+            guard let `self` = self else { return }
192
+            self.groupAvatar.setImage(avatar)
193
+        }).disposed(by: disposeBag)
297 194
     }
298
-
299
-    func returnCurrentIndex(index: Int) {
300
-        refreshUI(index: index)
195
+    
196
+    func bindgingViewModelToUserAvatar() {
197
+        viewModel.userAvatar
198
+            .subscribe(onNext: {[weak self] (avatar) in
199
+                guard let `self` = self else { return }
200
+                self.userAvatar.setImage(avatar)
201
+            }).disposed(by: disposeBag)
301 202
     }
302
-
303
-    func pushNext() {
304
-        let ctl = UIStoryboard.main.instantiateController(GroupViewController.self)
305
-
306
-//        ctl.groupModel = GroupModel(map: Map(mappingType: .fromJSON, JSON: datas[currentPhotoIndex].toJSON()))
307
-        show(ctl, sender: nil)
203
+    
204
+    func bindViewModelToUserName() {
205
+        viewModel.groupName.bind(to: userName.rx.text).disposed(by: disposeBag)
308 206
     }
309
-}
310
-
311
-//MARK textField delegate
312
-extension PhotoDetailViewController: UIGestureRecognizerDelegate {
313
-    // MARK: textField
314
-
315
-    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
316
-        return commentTextField.isFirstResponder
207
+    
208
+    func bindViewModelToPhotoTime() {
209
+        viewModel.photoTime.bind(to: photoTime.rx.text).disposed(by: disposeBag)
317 210
     }
318
-
319
-    @IBAction func ReturnKeyboard(_ sender: UITapGestureRecognizer) {
320
-        if !commentView.isHidden {
321
-            commentTextField.resignFirstResponder()
322
-            commentView.isHidden = true
323
-        }
211
+    
212
+    func bindViewModelToThumbupCount() {
213
+        viewModel.thumbupCount.bind(to: thumbupCount.rx.text).disposed(by: disposeBag)
324 214
     }
325
-
326
-}
327
-
328
-// MARK: UITableView delegate
329
-extension PhotoDetailViewController: UITableViewDataSource, UITableViewDelegate {
330
-
331
-    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
332
-        if section == 3 {
333
-            let cell = tableView.dequeueReusableCell(withIdentifier: "thumbupHeadCell")
334
-            if let label = cell?.viewWithTag(1001) as? UILabel {
335
-//                label.text = "(\(detailPageViewModel.thumbups.count))"
336
-            }
337
-            if let button = cell?.viewWithTag(1011) as? UIButton {
338
-                button.addTarget(self, action: #selector(showThumps), for: .touchUpInside)
339
-            }
340
-            if let imageView = cell?.viewWithTag(1008) as? UIImageView {
341
-//                let imageName = detailPageViewModel.thumbupsCount <= 0 ? "收起" : "list-arrow"
342
-//                imageView.image = UIImage(named : imageName)
343
-            }
344
-            return cell?.contentView
345
-        } else if section == 4 {
346
-            let cell = tableView.dequeueReusableCell(withIdentifier: "comentHeadCell")
347
-            if let label = cell?.viewWithTag(1002) as? UILabel {
348
-//                label.text = "(\(detailPageViewModel.comments.count))"
349
-            }
350
-            if let button = cell?.viewWithTag(1012) as? UIButton {
351
-                button.addTarget(self, action: #selector(showComments), for: .touchUpInside)
352
-            }
353
-            if let imageView = cell?.viewWithTag(1009) as? UIImageView {
354
-//                let imageName = detailPageViewModel.commentsCount <= 0 ? "收起" : "list-arrow"
355
-//                imageView.image = UIImage(named : imageName)
356
-            }
357
-            return cell?.contentView
358
-        }
359
-        return nil
215
+    
216
+    func bindViewModelToThumbupView() {
217
+        viewModel.thumbupItems
218
+            .asDriver(onErrorJustReturn: [])
219
+            .drive(onNext: { [weak self] (items) in
220
+                self?.setupThumbupView(items: items)
221
+            }).disposed(by: disposeBag)
360 222
     }
361
-
362
-    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
363
-        if section == 3 || section == 4 {
364
-            return 44
365
-        }
366
-        return 0
223
+    
224
+    func bindViewModelToCommentCount() {
225
+        viewModel.commentCount.bind(to: commentCount.rx.text).disposed(by: disposeBag)
367 226
     }
368
-
369
-    func numberOfSections(in tableView: UITableView) -> Int {
370
-        return 0
227
+    
228
+    func bindViewModelToCommentTableView() {
229
+        viewModel.commentItems
230
+            .bind(to: commentTableView.rx.items(dataSource: commentTableViewDataSource))
231
+            .disposed(by: disposeBag)
371 232
     }
372
-
373
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
374
-//        if section == 3 {
375
-//            return detailPageViewModel.thumbupsCount > 0 ? 1 : 0
376
-//        } else if section == 4 {
377
-//            return detailPageViewModel.commentsCount
378
-//        }
379
-        return 0
233
+    
234
+    func bindBuyViewIsVisiable() {
235
+        viewModel.canBuy.map { !$0 }.bind(to: buyView.rx.isHidden).disposed(by: disposeBag)
380 236
     }
381
-
382
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
383
-        if indexPath.section == 0 {
384
-            let cell = tableView.dequeueReusableCell(withIdentifier: "headCell", for: indexPath) as! DetailPageHeadCell
385
-            cell.enterView.isHidden = isHiddenEnterView
386
-//            cell.setInfo(datas[currentPhotoIndex])
387
-            cell.delegate = self
388
-            if let reportBtn = cell.viewWithTag(40001) as? UIButton {
389
-
390
-                reportBtn.addTarget(self, action: #selector(loadReportController), for: .touchUpInside)
391
-//                reportBtn.isHidden = !(UserDefaults.Account.bool(forKey: .isAudit))
392
-            }
393
-            return cell
394
-        } else if indexPath.section == 1 {
395
-            let cell = tableView.dequeueReusableCell(withIdentifier: "detailPagePhotoCell", for: indexPath) as! DetailPagePhotoCell
396
-//            cell.datas = datas
397
-//            cell.currentPhotoIndex = currentPhotoIndex
398
-            cell.delegate = self
399
-            cell.first = true
400
-            cell.collectionView.reloadData()
401
-            return cell
402
-        } else if indexPath.section == 2 {
403
-            let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! DetailPageNameCell
404
-//            cell.setInfo(datas[currentPhotoIndex])
405
-            return cell
406
-        } else if indexPath.section == 3 {
407
-            let cell = tableView.dequeueReusableCell(withIdentifier: "thumbupCell", for: indexPath) as! DetailthumbupImagesCell
408
-//            if detailPageViewModel.thumbups.count > 0 {
409
-//                let headers = detailPageViewModel.thumbups.map {$0.avatar}
410
-//                cell.setInfo(content: headers)
411
-//            }
412
-            return cell
413
-        } else {
414
-            let cell = tableView.dequeueReusableCell(withIdentifier: "comentCell", for: indexPath) as! DetailCommentCell
415
-//            cell.setInfo(detailPageViewModel.comments[indexPath.row])
416
-            return cell
417
-        }
237
+    
238
+    func bindCommentTextFieldToSendBtn() {
239
+        commentTextField.rx.text
240
+            .map { !($0?.isEmpty)! }
241
+            .bind(to: sendBtn.rx.isEnabled)
242
+            .disposed(by: disposeBag)
243
+    }
244
+    
245
+    func bindCollectionViewDelegate() {
246
+        photoCollectionView.rx.setDelegate(self).disposed(by: disposeBag)
247
+        
248
+    }
249
+    
250
+    func bindListViewModelToCollectionView() {
251
+        listViewModel.content
252
+            .bind(to: photoCollectionView.rx.items(dataSource: photoCollectionViewDataSource))
253
+            .disposed(by: disposeBag)
254
+    }
255
+    
256
+    func bindCollectionViewToListViewModel() {
257
+        photoCollectionView.rx.willDisplayCell
258
+            .asDriver()
259
+            .drive(onNext: { [unowned self] in
260
+                self.listViewModel.willShow(index: $0.at.row)
261
+            })
262
+            .disposed(by: disposeBag)
263
+    }
264
+    
265
+    func bindCollectionViewSelected() {
266
+        photoCollectionView.rx.itemSelected
267
+            .asDriver(onErrorJustReturn: IndexPath(item: 0, section: 0))
268
+            .drive(onNext: { [unowned self] _ in self.listViewModel.didSelected() })
269
+            .disposed(by: disposeBag)
270
+    }
271
+    
272
+    func bindViewWillAppear() {
273
+        viewModel.viewWillAppear
274
+            .asDriver()
275
+            .drive(onNext: { [unowned self] _ in
276
+                self.photoCollectionView.scrollToItem(at: IndexPath(item: self.listViewModel.currIndex, section: 0), at: .right, animated: false)
277
+            })
278
+            .disposed(by: disposeBag)
418 279
     }
280
+    
281
+    func monitorKeyboardWillShow() {
282
+        NotificationCenter.default.rx
283
+            .notification(UIResponder.keyboardWillShowNotification)
284
+            .takeUntil(self.rx.deallocated)
285
+            .subscribe { [unowned self] notification in
286
+                guard let userInfo = notification.element?.userInfo,
287
+                    let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
288
+                    let timeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
289
+                    let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int
290
+                    else { return }
291
+                UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: curve) ?? .linear)
292
+                UIView.animate(withDuration: timeInterval, animations: {
293
+                    self.commentEditYConstraint.constant = keyboardFrame.height
294
+                    self.view.layoutIfNeeded()
295
+                })
296
+        }.disposed(by: disposeBag)
297
+    }
298
+    
299
+    func monitorKeyboardWillHide() {
300
+        NotificationCenter.default.rx
301
+            .notification(UIResponder.keyboardWillHideNotification)
302
+            .takeUntil(self.rx.deallocated)
303
+            .subscribe { [unowned self] notification in
304
+                guard let userInfo = notification.element?.userInfo,
305
+                    let timeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
306
+                    let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int
307
+                    else { return }
308
+                UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: curve) ?? .linear)
309
+                UIView.animate(withDuration: timeInterval, animations: {
310
+                    self.commentEditYConstraint.constant = -56
311
+                    self.view.layoutIfNeeded()
312
+                })
313
+                self.commentTextField.clear()
314
+        }.disposed(by: disposeBag)
315
+    }
316
+}
419 317
 
420
-    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
421
-        if indexPath.section == 0 {
422
-            return 48
423
-        } else if indexPath.section == 1 {
424
-            return 360
425
-        } else if indexPath.section == 2 {
426
-            return 36
427
-        } else if indexPath.section == 3 {
428
-            return 40
429
-        } else {
430
-//            return 40 + detailPageViewModel.comments[indexPath.row].cellHeigth
431
-            return 40
318
+extension PhotoDetailViewController {
319
+    func setupThumbupView(items: [PhotoThumbupUserItem]) {
320
+        thumbupView.subviews.forEach { $0.removeFromSuperview() }
321
+        
322
+        let row = (Int(kScreenWidth) - 6) / 34
323
+        var topConstraint: CGFloat = 6
324
+        var last: UIImageView?
325
+        
326
+        for (index, item) in items.enumerated() {
327
+            let imageView = UIImageView()
328
+            imageView.cornerRadius = 5
329
+            imageView.translatesAutoresizingMaskIntoConstraints = false
330
+            imageView.setImage(item.avatar, placeholder: UIImage.defaultAvatar)
331
+            thumbupView.addSubview(imageView)
332
+            
333
+            if index % row == 0 && index != 0 {
334
+                topConstraint += 28 + 6
335
+                last = nil
336
+            }
337
+            
338
+            NSLayoutConstraint.activate([
339
+                imageView.widthAnchor.constraint(equalToConstant: 28),
340
+                imageView.heightAnchor.constraint(equalToConstant: 28),
341
+                imageView.topAnchor.constraint(equalTo: thumbupView.topAnchor, constant: topConstraint),
342
+                imageView.leadingAnchor.constraint(equalTo: last?.trailingAnchor ?? thumbupView.leadingAnchor, constant: 6)
343
+                ])
344
+            
345
+            last = imageView
432 346
         }
347
+        thumbupViewHeightConstraint.constant = items.isEmpty ? 1 : topConstraint + 34
348
+        commentTableView.tableHeaderView?.height = 532 + thumbupViewHeightConstraint.constant
433 349
     }
434 350
 }
435 351
 
436
-extension PhotoDetailViewController {
352
+extension PhotoDetailViewController: UICollectionViewDelegateFlowLayout {
353
+    func collectionView(_ collectionView: UICollectionView,
354
+                        layout collectionViewLayout: UICollectionViewLayout,
355
+                        sizeForItemAt indexPath: IndexPath) -> CGSize {
356
+        return CGSize(width: kScreenWidth, height: 359)
357
+    }
437 358
     
359
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
360
+        return 0
361
+    }
438 362
 }
363
+
364
+extension PhotoDetailViewController: NavigationBackViewController {}

+ 90 - 75
PaiAi/Paiai_iOS/App/PhotoDetail/PhotoPreviewViewController.swift

@@ -1,5 +1,5 @@
1 1
 //
2
-//  ShowFullPicController.swift
2
+//  PhotoPreviewViewController.swift
3 3
 //  PaiAi
4 4
 //
5 5
 //  Created by zhengjianfei on 16/4/9.
@@ -9,49 +9,104 @@
9 9
 import UIKit
10 10
 import PaiaiDataKit
11 11
 import PaiaiUIKit
12
+import RxCocoa
13
+import RxSwift
14
+import RxDataSources
12 15
 
13
-final class ShowFullPicController: UIViewController {
16
+final class PhotoPreviewViewController: UIViewController {
14 17
 
15
-    // MARK: Storyboard property
18
+    /// MARK: Storyboard property
16 19
     @IBOutlet weak var collectionView: UICollectionView!
17
-//    @IBOutlet weak var progressView: FFProgress!
18
-   // MARK: parameter property
19
-    lazy var datas = [PhotoItem]()
20
-    lazy var currentPhotoIndex = 0
21
-    lazy var firstLayout = true
22
-    lazy var currentPageIndex = 0
23
-    lazy var showNomark = false
24
-    lazy var showHD = false
25
-
26
-    var shufflingImage = [String]()
27
-
28
-    // MARK: Controller fucntion
20
+    var viewModel: PhotoDetailListViewModel!
21
+    var disposeBag = DisposeBag()
22
+    
23
+    override var prefersStatusBarHidden: Bool {
24
+        return true
25
+    }
26
+    
29 27
     override func viewDidLoad() {
30 28
         super.viewDidLoad()
31
-        navigationController?.isNavigationBarHidden = true
32
-//        titleWithbackBar = ""
33
-
34
-     }
35
-
36
-    override func viewDidLayoutSubviews() {
37
-        if firstLayout {
38
-            collectionView.contentOffset = CGPoint(x: (CGFloat(currentPhotoIndex) * (collectionView.width)), y: 0)
39
-            firstLayout = false
29
+        binding()
30
+        scrollToSpecifiedImage()
31
+        navigationController?.setNavigationBarHidden(true, animated: true)
32
+        
33
+    }
34
+    
35
+    func scrollToSpecifiedImage() {
36
+        collectionView.layoutIfNeeded()
37
+        collectionView.scrollToItem(at: IndexPath(item: viewModel.currIndex, section: 0), at: .right, animated: false)
38
+    }
39
+    
40
+    override func viewDidAppear(_ animated: Bool) {
41
+        super.viewWillAppear(animated)
42
+        bindCollectionViewToViewModel()
43
+    }
44
+    
45
+    override func viewWillDisappear(_ animated: Bool) {
46
+        super.viewWillDisappear(animated)
47
+        navigationController?.setNavigationBarHidden(false, animated: true)
48
+    }
49
+    
50
+    @IBAction func download(_ sender: UIButton) {
51
+        guard let cell = collectionView.cellForItem(at: IndexPath(item: viewModel.currIndex, section: 0)) as? ImageCell,
52
+            let image = cell.photoImage.image else {
53
+                //            FFToastView.showToast(inView: view, withText: "未检测到图片")
54
+                return
40 55
         }
41
-        super.viewDidLayoutSubviews()
56
+        
57
+        UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
42 58
     }
59
+    
60
+    @objc func image(_ image: UIImage?, didFinishSavingWithError error: NSError?, contextInfo info: UnsafeMutableRawPointer) {
61
+        if error != nil {
62
+            //            FFToastView.showToast(inView: view, withText: "保存图片失败")
63
+        } else {
64
+            //            FFToastView.showImageToast(inView: view, withText: "已保存图片到相册", withImage: "提示弹窗-勾")
65
+        }
66
+    }
67
+}
68
+
69
+/// binding
70
+extension PhotoPreviewViewController {
71
+    
72
+    var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>> {
73
+        return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>>(configureCell: { (dataSource, collectionView, indexPath, item) in
74
+            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
75
+            cell.setModel(url: item.murl.isEmpty ? item.photo_url : item.murl)
76
+            return cell
77
+        })
78
+    }
79
+    
80
+    func binding() {
81
+        bindViewModelToCollectionView()
82
+        bindCollectionViewDelegate()
83
+    }
84
+    
85
+    func bindViewModelToCollectionView() {
86
+        viewModel.content
87
+            .bind(to: collectionView.rx.items(dataSource: dataSource))
88
+            .disposed(by: disposeBag)
89
+    }
90
+    
91
+    func bindCollectionViewDelegate() {
92
+        collectionView.rx.setDelegate(self).disposed(by: disposeBag)
93
+    }
94
+    
95
+    func bindCollectionViewToViewModel() {
96
+        collectionView.rx.willDisplayCell
97
+            .asDriver()
98
+            .drive(onNext: { [unowned self] in self.viewModel.willShow(index: $0.at.row) })
99
+            .disposed(by: disposeBag)
100
+    }
101
+}
43 102
 
44
-    // MARK: Storyboard  button function
103
+/// storyboard button action
104
+extension PhotoPreviewViewController {
45 105
     @IBAction  func back() {
46
-         _ = navigationController?.popViewController(animated: true)
47
-        guard let ctl = navigationController?.viewControllers.last as? PhotoDetailViewController else {
48
-            return
49
-        }
50
-//        ctl.currentPhotoIndex = currentPageIndex
51
-//        ctl.tableView.reloadData()
106
+        navigationController?.popViewController(animated: true)
52 107
     }
53 108
     @IBAction func rotateTheImage(_ sender: UIButton) {
54
-        guard let cell = collectionView.cellForItem(at: IndexPath(item: currentPageIndex, section: 0)) as? ImageCell else {
109
+        guard let cell = collectionView.cellForItem(at: IndexPath(item: viewModel.currIndex, section: 0)) as? ImageCell else {
55 110
             return
56 111
         }
57 112
         UIView.beginAnimations("image.rotate", context: nil)
@@ -71,54 +126,14 @@ final class ShowFullPicController: UIViewController {
71 126
             }
72 127
         }
73 128
     }
129
+    
74 130
 
75
-    @IBAction func load() {
76
-        guard let cell = collectionView.cellForItem(at: IndexPath(item: currentPageIndex, section: 0)) as? ImageCell else {
77
-//            FFToastView.showToast(inView: view, withText: "未检测到图片")
78
-            return
79
-        }
80
-        guard let image = cell.photoImage.image else {
81
-//            FFToastView.showToast(inView: view, withText: "未检测到图片")
82
-            return
83
-        }
84
-        UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
85
-    }
86
-
87
-    @objc func image(_ image: UIImage?, didFinishSavingWithError error: NSError?, contextInfo info: UnsafeMutableRawPointer) {
88
-        if error != nil {
89
-//            FFToastView.showToast(inView: view, withText: "保存图片失败")
90
-        } else {
91
-//            FFToastView.showImageToast(inView: view, withText: "已保存图片到相册", withImage: "提示弹窗-勾")
92
-        }
93
-
94
-    }
95 131
 }
96 132
 
97 133
 // MARK: UICollectionView delegate
98
-extension ShowFullPicController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
99
-
100
-    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
101
-        return shufflingImage.count > datas.count ? shufflingImage.count : datas.count
102
-    }
103
-
104
-    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
105
-        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
106
-        if shufflingImage.count <= 0 {
107
-            let data = datas[indexPath.item]
108
-            let urlStr = data.murl.isEmpty ? data.photo_url : data.murl
109
-            cell.setModel(url: urlStr)
110
-        } else {
111
-            cell.setModel(url: shufflingImage[indexPath.row])
112
-        }
113
-        return cell
114
-    }
134
+extension PhotoPreviewViewController: UICollectionViewDelegateFlowLayout {
115 135
 
116 136
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
117 137
         return CGSize(width: collectionView.width, height: collectionView.height - 20)
118 138
     }
119
-
120
-    func scrollViewDidScroll(_ scrollView: UIScrollView) {
121
-        let page = Int(scrollView.contentOffset.x / (collectionView.width))
122
-        currentPageIndex = page
123
-    }
124 139
 }

+ 15 - 7
PaiAi/Paiai_iOS/App/PhotoDetail/ShareView.swift

@@ -9,13 +9,21 @@
9 9
 import UIKit
10 10
 
11 11
 class ShareView: UIView {
12
+    
13
+}
14
+
12 15
 
13
-    /*
14
-    // Only override draw() if you perform custom drawing.
15
-    // An empty implementation adversely affects performance during animation.
16
-    override func draw(_ rect: CGRect) {
17
-        // Drawing code
18
-    }
19
-    */
20 16
 
17
+extension ShareView {
18
+    
19
+    func activateConstraints() {
20
+        
21
+    }
22
+    
23
+    func activateConstraintsShareView() {
24
+        guard let superView = superview else { return }
25
+        
26
+        self.translatesAutoresizingMaskIntoConstraints = false
27
+        
28
+    }
21 29
 }

+ 0 - 15
PaiAi/launch_multiple_simulators.sh

@@ -1,15 +0,0 @@
1
-#!/bin/bash
2
-
3
-xcrun simctl shutdown all
4
-
5
-path=$(find ~/Library/Developer/Xcode/DerivedData/Paiai-*/Build/Products/Debug-iphonesimulator -name "Paiai.app" | head -n 1)
6
-echo "${path}"
7
-
8
-filename=MultiSimConfig.txt
9
-grep -v '^#' $filename | while read -r line
10
-do
11
-  echo $line
12
-  xcrun instruments -w "$line"
13
-  xcrun simctl install booted $path
14
-  xcrun simctl launch booted com.Paiai.Paiai
15
-done

add admin/user/result · 247809d430 - Gogs: Go Git Service

add admin/user/result

FFIB 3 年 前
コミット
247809d430
共有2 個のファイルを変更した35 個の追加0 個の削除を含む
  1. 29 0
      api/point_views.py
  2. 6 0
      api/urls.py

+ 29 - 0
api/point_views.py

@@ -136,3 +136,32 @@ def mp_upload_temperature(request):
136 136
     ipui.save()
137 137
 
138 138
     return response()
139
+
140
+
141
+def admin_user_results(request):
142
+    point_id = request.POST.get('point_id', '')
143
+    kw = request.POST.get('kw', '')
144
+    page = request.POST.get('page', 1)
145
+    num = request.POST.get('num', 20)
146
+
147
+    ipuis = IsolationPointUserInfo.objects.filter(point_id=point_id, status=True)
148
+
149
+    if kw:
150
+        ipuis = ipuis.filter(fields__icontains=kw)
151
+    
152
+    total_active_eqpt_num = ipuis.count()
153
+    ipuis, left = pagination(ipuis, page, num)
154
+
155
+    ipuis = [ipui.data for ipui in ipuis]
156
+
157
+    has_upload_temperature_num = len([1 for ipui in ipuis if ipui.get('temperature_has_upload', '') == IsolationPointUserInfo.HAS_YET_UPLOAD])
158
+    fever_num = len([1 for ipui in ipuis if ipui.get('temperature', 0) > settings.FEVER_TEMPERATURE])
159
+
160
+    return response(data={
161
+        'ipuis': ipuis,
162
+        'left': left,
163
+        'total_active_eqpt_num': total_active_eqpt_num,
164
+        'has_upload_temperature_num': has_upload_temperature_num,
165
+        'not_upload_temperature_num': total_active_eqpt_num - has_upload_temperature_num,
166
+        'fever_num': fever_num,
167
+    })

+ 6 - 0
api/urls.py

@@ -47,6 +47,12 @@ urlpatterns += [
47 47
     url(r'^upload/temperature$', eqpt_views.upload_temperature, name='upload_temperature'),
48 48
 ]
49 49
 
50
+#Admin
51
+urlpatterns += [
52
+    url(r'^admin/user/result$', point_views.admin_user_results, name='admin_user_results')
53
+]
54
+
55
+
50 56
 # Mini App
51 57
 urlpatterns += [
52 58
     url(r'^mp/userinfo$', mini_views.get_userinfo_api, name='get_userinfo_api'),  # 获取用户信息

:art: Change chg_sta default False · c999d00d20 - Gogs: Go Git Service

:art: Change chg_sta default False

huangqimin001 4 年之前
父节点
当前提交
c999d00d20
共有 2 个文件被更改,包括 20 次插入2 次删除
  1. 18 0
      equipment/migrations/0017_alter_isolationpointuserinfo_chg_sta.py
  2. 2 2
      equipment/models.py

+ 18 - 0
equipment/migrations/0017_alter_isolationpointuserinfo_chg_sta.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.6 on 2021-08-17 15:35
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('equipment', '0016_isolationpointuserinfo_chg_sta'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AlterField(
14
+            model_name='isolationpointuserinfo',
15
+            name='chg_sta',
16
+            field=models.BooleanField(default=False, help_text='充电状态,true 充电,false 未充电', verbose_name='chg_sta'),
17
+        ),
18
+    ]

+ 2 - 2
equipment/models.py

@@ -82,7 +82,7 @@ class IsolationPointInfo(BaseModelMixin):
82 82
 class IsolationPointUserInfo(BaseModelMixin):
83 83
     HAS_NOT_UPLOAD = '未上报'
84 84
     HAS_YET_UPLOAD = '已上报'
85
-    CHG_STA_CHARGING = ' 充电中'
85
+    CHG_STA_CHARGING = '充电中'
86 86
 
87 87
     point_id = models.CharField(_('point_id'), max_length=32, blank=True, null=True, help_text='隔离点唯一标识', db_index=True)
88 88
 
@@ -93,7 +93,7 @@ class IsolationPointUserInfo(BaseModelMixin):
93 93
     observed_ymds = JSONField(_('observed_ymds'), default=[], blank=True, null=True, help_text='已测温日期')
94 94
     observed_days = models.IntegerField(_('observed_days'), default=0, help_text='已测温天数')
95 95
 
96
-    chg_sta = models.BooleanField(_(u'chg_sta'), default=True, help_text='充电状态,true 充电,false 未充电')
96
+    chg_sta = models.BooleanField(_(u'chg_sta'), default=False, help_text='充电状态,true 充电,false 未充电')
97 97
     temperature = models.FloatField(_('temperature'), default=0, help_text='用户体温')
98 98
     last_submit_at = models.DateTimeField(_('last_submit_at'), blank=True, null=True, help_text='上一次上报时间')
99 99
     leave_at = models.DateTimeField(_('leave_at'), blank=True, null=True, help_text='离开时间')